package com.example.filesftp.ftptransfer.util;


import com.example.common.core.util.R;
import com.example.filesftp.config.NybgftpConfig;
import com.example.filesftp.ftptransfer.domain.entity.FileTransferConfig;
import com.mchange.v2.lang.StringUtils;
import lombok.extern.slf4j.Slf4j;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.sftp.FileAttributes;
import net.schmizz.sshj.sftp.RemoteResourceInfo;
import net.schmizz.sshj.sftp.SFTPClient;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.springframework.stereotype.Component;

import java.io.*;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.jcraft.jsch.*;

import javax.tools.FileObject;

@Slf4j
@Component
public class SftpUtil {


    private final String windowsSeparator = "\\\\";
    private final String linuxSeparator = "/";

    public R sftpDownloadFile(FileTransferConfig config,LocalDate date) throws IOException {
        //把date转换成第二天0点的起报时间
        LocalDateTime publishTime =  date.plusDays(1).atStartOfDay();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHH");
        String publishTimeStr = publishTime.format(formatter);
        //远程操作系统
        String remoteType = config.getRemoteType();
        //本地操作系统
        String localType = config.getLocalType();

        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        String createTimeStr = date.format(dateTimeFormatter);
        //csv文件名
        String csvFileName = config.getCsvFileName();
        //指定文件名
        String designateFileName = config.getFileName();

        //获取配置文件中的实体类
//        List<config> files = nybgftpConfig.getFiles();
//        for (NybgftpConfig.FileInfo file : files) {

        String dynamicPattern = null;
        Pattern filePattern = null;
        //指定文件下载
        if ( StringUtils.nonEmptyString(designateFileName)){
            dynamicPattern = designateFileName;
            filePattern  = Pattern.compile(dynamicPattern);
        }
        //csv文件下载
        if (StringUtils.nonEmptyString(csvFileName)){
            dynamicPattern = csvFileName
                    .replace("(?<time>\\d{8})", createTimeStr)
                    .replace("(?<date>\\d{10})", publishTimeStr);
            filePattern  = Pattern.compile(dynamicPattern);
        }

        // 确定操作系统类型
        boolean isRemoteWindows = "1".equals(config.getRemoteType());
        boolean isLocalWindows = "1".equals(config.getLocalType());

        // 处理路径
        String remotePath = PathUtils.toRemotePath(config.getRemotePath(), isRemoteWindows);
        String localPath = PathUtils.toLocalPath(config.getLocalPath(), isLocalWindows);

        // 检查本地目录
        Path localDirPath = Paths.get(config.getLocalPath());
        if (!Files.exists(localDirPath)) {
            try {
                Files.createDirectories(localDirPath);
            } catch (IOException e) {
                log.error("创建本地目录失败: {}", localDirPath, e);
                return R.failed("创建本地目录失败");
            }
        }


        // 检查目录下所有文件
        boolean isFileExists = false;
        List<File> allFile = new ArrayList<>();
        try (Stream<Path> pathStream = Files.list(localDirPath)) {
            pathStream.forEach(path -> allFile.add(path.toFile()));
            if (filePattern != null){
                Pattern finalFilePattern1 = filePattern;
                isFileExists =allFile.stream().anyMatch(file -> finalFilePattern1.matcher(file.getName()).matches());
            }
        } catch (IOException e) {
            log.error("目录遍历失败: {}", localDirPath, e);
            return R.failed("目录遍历失败");
        }

        if (isFileExists) {
            log.info("已存在匹配文件，跳过下载，目录: {}", localDirPath);
//            return R.failed("已存在匹配文件，跳过下载，目录: "+localDirPath);
        }


        SFTPClient sftpClient  = null;
        SSHClient sshclient=null;
        ChannelSftp channel = null;
        try{
            sshclient = FileByteUtil.getSSHClient(config.getHost(), config.getUsername(),
                    config.getPassword());
            sftpClient =sshclient.newSFTPClient();

            // 列出远程目录文件

            List<RemoteResourceInfo> remoteFiles = sftpClient.ls(remotePath);
            log.info("远程目录 {} 下的文件:", remotePath);
            remoteFiles.forEach(f -> log.info(f.getName()));
            List<Optional<RemoteResourceInfo>> matchedFileOptList = new ArrayList<>();
            if (filePattern != null){
                // 查找匹配的远程文件
                Pattern finalFilePattern = filePattern;
                Optional<RemoteResourceInfo> matchedFileOpt = remoteFiles.stream()
                        .filter(f -> {
                            String filename = f.getName();
                            return finalFilePattern.matcher(filename).matches();
                        }).findFirst();

                if (!matchedFileOpt.isPresent()) {
                    log.info("未找到远程的当日文件");
                    return R.failed("未找到远程的当日文件");
                }
                matchedFileOptList.add(matchedFileOpt);
            }else {
                for (RemoteResourceInfo remoteFile : remoteFiles) {
                    String remoteFileName = remoteFile.getName();
                    FileAttributes attributes = remoteFile.getAttributes();
                    long remoteFileSize = attributes.getSize();
                    File file = allFile.stream().filter(f -> f.getName().equals(remoteFileName)).findFirst().orElse(null);
                    long fileSize = 0;
                    if (file != null) {
                        fileSize = file.length();
                        long lastModified  = file.lastModified();
                        long remoteFileLastModified = attributes.getMtime()*1000L;
                        if (file.isDirectory()){
                            fileSize = Files.walk(file.toPath()).mapToLong(p -> p.toFile().length())
                                    .sum();
                            remoteFileSize = getFolderSize(sftpClient, remoteFile.getPath());

                        }
                        if (file.getName().equals(remoteFileName) && remoteFileSize == fileSize && remoteFileLastModified == lastModified) {
                            continue;

                        }
                    }
                    Optional<RemoteResourceInfo> matchedFileOpt = Optional.of(remoteFile);
                    matchedFileOptList.add(matchedFileOpt);
                }
            }
            for (Optional<RemoteResourceInfo> remoteResourceInfo : matchedFileOptList) {
                // 下载文件
                RemoteResourceInfo matchedFile = remoteResourceInfo.get();
                log.info("找到匹配文件: {}", matchedFile.getName());

                // 执行下载
                Path localFilePath = localDirPath.resolve(matchedFile.getName());
                sftpClient.get(matchedFile.getPath(), localFilePath.toString());
                log.info("文件下载成功: {} → {}", matchedFile.getPath(), localFilePath);
            }
        }catch (Exception ex){
            ex.getLocalizedMessage();
            log.error(ex.getLocalizedMessage(),ex);
        }finally {
            try{
                if(sftpClient != null){
                    sftpClient.close();
                }
                if(sshclient != null){
                    sshclient.close();
                }
            }catch (Exception e){
                e.printStackTrace();
                log.error(e.getLocalizedMessage(),e);
            }
        }

        return R.ok("执行成功");
    }



    public R sftpUploadFile(FileTransferConfig config, LocalDate date) throws JSchException, IOException,
            SftpException {
        //把date转换成第二天0点的起报时间
        LocalDateTime publishTime =  date.plusDays(1).atStartOfDay();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHH");
        String publishTimeStr = publishTime.format(formatter);


        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        String createTimeStr = date.format(dateTimeFormatter);
        //csv文件名
        String csvFileName = config.getCsvFileName();
        //指定文件名
        String designateFileName = config.getFileName();
        // 1. 创建并连接SFTP会话
        Session session = null;
        ChannelSftp sftpChannel = null;

        boolean isRemoteWindows = "1".equals(config.getRemoteType());
        boolean isLocalWindows = "1".equals(config.getLocalType());
        //获取配置文件中的实体类
        try {
            JSch jsch = new JSch();
            session = jsch.getSession(config.getUsername(), config.getHost(), config.getPort());
            session.setPassword(config.getPassword());
            session.setConfig("StrictHostKeyChecking", "no");
            session.connect();
            // 2. 打开SFTP通道
            sftpChannel = (ChannelSftp) session.openChannel("sftp");
            sftpChannel.connect();
            // 3. 确保远程基础目录存在

            // 4. 处理本地路径（文件或目录）
            String localPath = PathUtils.toLocalPath(config.getLocalPath(), isLocalWindows);
            String remoteBaseDir = config.getRemotePath();
//            if ("1".equals(config.getRemoteType())) { // Windows系统
//                // 确保路径以驱动器字母开头
//                if (!remoteBaseDir.matches("^[A-Za-z]:\\\\.*")) {
//                    log.error("Windows系统必须指定驱动器字母路径，如F:\\path\\to\\dir");
//                    return R.failed("无效的Windows路径格式");
//                }
//
//                remoteBaseDir = PathUtils.toWindowsRemotePath(remoteBaseDir);
//                log.debug("转换后的远程路径: {}", remoteBaseDir);
//            }

            createRemoteDir(sftpChannel,remoteBaseDir,isRemoteWindows);
            if (StringUtils.nonEmptyString(designateFileName)){
                localPath = localPath + File.separator + designateFileName;
            }else if (StringUtils.nonEmptyString(csvFileName)){
                localPath = localPath + File.separator +
                        csvFileName.replace("${publishTime}",publishTimeStr).replace("${createTime}",createTimeStr);
            }
            File localFile = new File(localPath);
            if (localFile.isFile()) {
                // 上传单个文件
                uploadFile(sftpChannel, remoteBaseDir, localFile);
            } else if (localFile.isDirectory()) {
                // 上传整个目录
                uploadDirectory(sftpChannel, remoteBaseDir, localFile,isRemoteWindows);
            } else {
                System.err.println("本地路径既不是文件也不是目录: " + localPath);
            }
        }catch (Exception e){
            log.error("SFTP操作出错: " + e.getMessage());
            e.printStackTrace();
        }finally {
            // 关闭连接
            if (sftpChannel != null && sftpChannel.isConnected()) {
                sftpChannel.disconnect();
            }
            if (session != null && session.isConnected()) {
                session.disconnect();
            }
        }
        return R.ok("执行成功");
    }

    /**
     * 递归创建远程目录
     */
    private static void createRemoteDir(ChannelSftp sftpChannel, String remoteDir, boolean isRemoteWindows)
            throws SftpException {
        // 特殊处理Windows驱动器路径
        String normalizedPath;
        if (isRemoteWindows && PathUtils.isValidWindowsDrivePath(remoteDir)) {
            normalizedPath = PathUtils.toWindowsRemotePath(remoteDir);
        } else {
            normalizedPath = PathUtils.toRemotePath(remoteDir, isRemoteWindows);
        }

        String[] folders = normalizedPath.split("/");
        StringBuilder pathBuilder = new StringBuilder();

        for (String folder : folders) {
            if (folder.isEmpty()) {
                continue;
            }

            pathBuilder.append("/").append(folder);
            String currentPath = pathBuilder.toString();

            try {
                sftpChannel.lstat(currentPath);
                log.debug("目录已存在: {}", currentPath);
            } catch (SftpException e) {
                if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
                    log.info("创建远程目录: {}", currentPath);
                    sftpChannel.mkdir(currentPath);
                } else {
                    log.error("目录检查失败: {}", currentPath, e);
                    throw e;
                }
            }
        }
    }
    /**
     * 上传单个文件
     */
    private static void uploadFile(ChannelSftp sftpChannel, String remoteDir, File localFile) throws SftpException {
//        String remoteFilePath = remoteDir +localFile.getName();
        String remoteFilePath = remoteDir +"/"+ localFile.getName();
        // 检查文件是否已存在
        if (isFileExists(sftpChannel, remoteFilePath)) {
            SftpATTRS sftpATTRS = sftpChannel.lstat(remoteFilePath);
            long localFileModifiedTime = 0;
            long remoteFileModifiedTime = 0;
            if (sftpATTRS != null && localFile != null) {
                localFileModifiedTime = localFile.lastModified();
                remoteFileModifiedTime = sftpATTRS.getMTime() * 1000L;
            }
            if (localFileModifiedTime >= remoteFileModifiedTime) {
                System.out.println("文件已存在或者已修改，跳过上传: " + remoteFilePath);
                return;
            }
        }

        // 上传文件
        System.out.println("上传文件: " + localFile.getPath() + " -> " + remoteFilePath);
        try (FileInputStream fis = new FileInputStream(localFile)) {
            sftpChannel.put(fis, remoteFilePath);
        } catch (Exception e) {
            throw new SftpException(0, "文件上传失败: " + e.getMessage());
        }
    }

    /**
     * 检查远程文件是否存在
     */
    private static boolean isFileExists(ChannelSftp sftpChannel, String remotePath) throws SftpException {
        try {
            sftpChannel.lstat(remotePath);
            return true;
        } catch (SftpException e) {
            if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
                return false;
            }
            throw e;
        }
    }

    /**
     * 递归上传目录及其内容
     */
    private static void uploadDirectory(ChannelSftp sftpChannel, String remoteBaseDir,
                                        File localDir, boolean isRemoteWindows) throws SftpException {
        Stack<File> dirStack = new Stack<>();
        Stack<String> remoteDirStack = new Stack<>();

        dirStack.push(localDir);
        remoteDirStack.push(remoteBaseDir);

        while (!dirStack.isEmpty()) {
            File currentLocalDir = dirStack.pop();
            String currentRemoteDir = remoteDirStack.pop();

            // 创建远程目录
            createRemoteDir(sftpChannel, currentRemoteDir, isRemoteWindows);
            log.info("处理目录: {} → {}", currentLocalDir.getPath(), currentRemoteDir);

            File[] localFiles = currentLocalDir.listFiles();
            if (localFiles == null) {
                continue;
                }

            for (File file : localFiles) {
                String remoteFilePath = currentRemoteDir + "/" + file.getName();
                if (file.isDirectory()) {
                    dirStack.push(file);
                    remoteDirStack.push(remoteFilePath);
                } else {
                    uploadFile(sftpChannel, currentRemoteDir, file);
                }
            }
        }
    }
    public static long getFolderSize(SFTPClient sftp, String remotePath) throws IOException {
        long size = 0;
        List<RemoteResourceInfo> resources = sftp.ls(remotePath);

        for (RemoteResourceInfo resource : resources) {
            if (resource.isDirectory()) {
                // 跳过 "." 和 ".." 目录
                if (!resource.getName().equals(".") && !resource.getName().equals("..")) {
                    // 递归计算子目录
                    size += getFolderSize(sftp, resource.getPath());
                }
            } else {
                size += resource.getAttributes().getSize();
            }
        }
        return size;
    }

}